
<!DOCTYPE html>
<html lang="">


<head><meta name="generator" content="Hexo 3.8.0">
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
  <meta name="theme-color" content="#202020">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <script src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script>
  
  
    <meta name="keywords" content>
  

  
    <meta name="description" content="破坏性测试">
  
  
  
  <link rel="icon" type="image/x-icon" href="/images/footer-logo.png">
  
  <title>破坏性测试 [ 51AIOps 专注于运维自动化  微信： kaipython ]</title>
  
    <!-- stylesheets list from config.yml -->
    
      <link rel="stylesheet" href="//cdn.bootcss.com/pure/1.0.0/pure-min.css">
    
      <link rel="stylesheet" href="/css/xoxo.css">
    
  
</head>


<body>
  <div class="nav-container">
    <nav class="home-menu pure-menu pure-menu-horizontal">
  <a class="pure-menu-heading" href="/">
    
    <span class="title" style="text-transform:none">51AIOps 专注于运维自动化  微信： kaipython</span>
  </a>

  <ul class="pure-menu-list clearfix">
      
          
            
              <li class="pure-menu-item"><a href="/" class="pure-menu-link">首页</a></li>
            
          
      
  </ul>
   
</nav>

  </div>

  <div class="container" id="content-outer">
    <div class="inner" id="content-inner" style='margin-left:-68px!important'>
      <div class="post-container">
  <article class="post" id="post">
    <header class="post-header text-center">
      <h1 class="title">
        破坏性测试
      </h1>
      <span>
        
        <time class="time" datetime="2020-08-16T16:00:00.000Z">
        2020-08-17
      </time>
        
      </span>
      <span class="slash">/</span>
      <span class="post-meta">
      <span class="post-tags">
        
      </span>
    </span>
      <span class="slash">/</span>
      <span class="read">
      <span id="busuanzi_value_page_pv"></span> 点击
    </span>
      <span class="slash">/</span>
    </header>

    <div class="post-content">
      <p>持续交付中涉及到的与测试相关的内容，包括了单元测试、自动化测试、冒烟测试等测试方法和理念，为什么我把破坏性测试拿出来，和你详细讨论呢？</p>
<p>原因无非包括两个方面：</p>
<p>其一，单元测试等传统测试方法，已经非常成熟了，而且你肯定也非常熟悉了；<br>其二，破坏性测试，变得越来越重要了。<br>那么，破坏性测试到底是因为什么原因变得原来越重要呢？</p>
<p>随着 SOA、微服务等架构的演进，分布式系统对测试的要求越来越高，不再像传统的单体应用测试一样，可以很容易地无缝嵌入到持续交付体系中。因为分布式系统的测试不仅需要大量的前提准备，还存在着非常严重的服务依赖问题。</p>
<p>这就使得分布式系统的测试工作，除了要关注运行的应用本身外，还要考虑测试环境的因素。</p>
<p>很快，我们就发现，破坏性测试可以解决分布式系统测试的这些难题，而且还可以帮助我们解决更多的问题。它可以弥补传统持续交付体系只关注代码或应用本身，而忽略其他外部因素影响运行中代码的问题。而且，破坏性测试还能很好地证明整个分布式系统的健壮性。</p>
<p>所以，与其老生长谈一些传统的测试方法，不如我们一起看看更新鲜、更好用的破坏性测试。</p>
<h3 id="什么是破坏性测试？"><a href="#什么是破坏性测试？" class="headerlink" title="什么是破坏性测试？"></a>什么是破坏性测试？</h3><blockquote>
<p>顾名思义，破坏性测试就是通过有效的测试手段，使软件应用程序出现奔溃或失败的情况，然后测试在这样的情况下，软件运行会产生什么结果，而这些结果又是否符合预期。</p>
</blockquote>
<p>这里需要注意的是，我们需要使用的测试手段必须是有效的。为什么这样说呢，有两点原因。</p>
<p>第一，破坏性测试的手段和过程，并不是无的放矢，它们是被严格设计和执行的。不要把破坏性测试和探索性测试混为一谈。也就是说，破坏性测试不应该出现，“试试这样会不会出问题”的假设，而且检验破坏性测试的结果也都应该是有预期的。</p>
<p>第二，破坏性测试，会产生切实的破坏作用，你需要权衡破坏的量和度。因为破坏不仅仅会破坏软件，还可能会破坏硬件。通常情况下，软件被破坏后的修复成本不会太大，而硬件部分被破坏后，修复成本就不好说了。所以，你必须要事先考虑好破坏的量和度。</p>
<h4 id="破坏性测试的流程与用例设计"><a href="#破坏性测试的流程与用例设计" class="headerlink" title="破坏性测试的流程与用例设计"></a>破坏性测试的流程与用例设计</h4><p>说到底，破坏性测试还是一种人为、事先设计的测试方法，所以它的流程与普通的软件测试流程基本一致：都包括设计测试用例、开发测试脚本、执行测试脚本、捕获缺陷、报告缺陷的过程。</p>
<p>破坏性测试与普通测试流程，唯一不同的是，绝大部分普通测试可以在测试失败后，继续进行其他的测试；而破坏性测试，则有可能无法恢复到待测状态，只能停止后续的测试。</p>
<p>所以，在持续交付的哪个步骤和阶段执行破坏性测试，就非常讲究了，你需要经过严密地设计和预判。</p>
<p>所以，在设计破坏性测试的测试用例时，我们通常会考虑两个维度：</p>
<p>第一个维度是，一个破坏点的具体测试，即设计一个或一组操作，能够导致应用或系统奔溃或异常。此时，你需要注意两个问题：</p>
<p>出现问题后的系统或软件是否有能力按预期捕获和处理异常；</p>
<p>确认被破坏的系统是否有能力按照预期设计进行必要的修复，以确保能够继续处理后续内容。</p>
<p>第二个维度是，整个系统的破坏性测试，我们通常会采用压力测试、暴力测试、阻断链路去除外部依赖等方法，试图找到需要进行破坏性测试的具体的点。</p>
<p>这两个维度的测试方法、流程基本一致，区别只是第二维度的测试通常不知道具体要测试的点，所以破坏范围会更大，甚至可能破坏整个系统。</p>
<h4 id="破坏性测试的执行策略"><a href="#破坏性测试的执行策略" class="headerlink" title="破坏性测试的执行策略"></a>破坏性测试的执行策略</h4><p>由于具有切实的破坏力这个特点，我们在执行破坏性测试时需要考虑好执行策略，以避免发生不可挽回的局面。</p>
<p>一般情况下，在发布前执行破坏性测试相对比较安全。但这也不是绝对的，比如你一不小心把 UAT 等大型联调环境搞坏了，其代价还是很可观的。</p>
<p>因此，绝大部分破坏性测试都会在单元测试、功能测试阶段执行。而执行测试的环境也往往是局部的测试子环境。</p>
<p>那么问题又来了，真实环境要比测试子环境更复杂多变，在测试子环境进行的破坏性测试真的有效吗？这真是一个极好的问题。</p>
<p>所以，最近几年，技术圈衍生出一个很流行的理论：混沌工程。</p>
<h3 id="混沌工程"><a href="#混沌工程" class="headerlink" title="混沌工程"></a>混沌工程</h3><p>随着分布式系统架构的不断进步，传统的破坏性测试也越发捉襟见肘，最主要的问题有两个：</p>
<p>第一，它被设计得太严格，以至于失真了。而真正有破坏力的故障，都是随机的、并行的、胡乱的。</p>
<p>第二，它覆盖不了生产环境，只能做到类似抽样检验的能力，且很难重复和持续。</p>
<p>所以，混沌工程的理论就应运而生了。</p>
<p>混沌工程是在分布式系统上建立的实验，其目的是建立对系统承受混乱冲击能力的信心。鉴于分布式系统固有的混乱属性，也就是说即使所有的部件都可以正常工作，但把它们结合后，你还是很难预知会发生什么。</p>
<p>所以，我们需要找出分布式系统的这些弱点。我把这些弱点归为了以下几类：</p>
<p>当服务不可用时，不可用或不完整的回退能力；<br>不合理的设置超时时间引起的重试风暴；<br>依赖服务接收过多的流量，从而导致中断；<br>由单个故障点引起的级联故障；<br>……<br>我们要避免这些弱点在生产过程中影响客户，所以需要一种方法来探知和管理这些系统固有的混乱，经实践证明，通过一些受控实验，我们能够观察这些弱点在系统中的行为。这种实验方法，就被叫作混沌工程。</p>
<p>说到具体的实验方法，需要遵循以下 4 个步骤，即科学实验都必须遵循的 4 个步骤：</p>
<ul>
<li><p>将正常系统的一些正常行为的可测量数据定义为“稳定态”；</p>
</li>
<li><p>建立一个对照组，并假设对照组和实验组都保持“稳定态”；</p>
</li>
<li><p>引入真实世界的变量，如服务器崩溃、断网、磁盘损坏等等；</p>
</li>
<li><p>尝试寻找对照组和实验组之间的差异，找出系统弱点。</p>
</li>
</ul>
<p>“稳定态”越难被破坏，则说明系统越稳固；而发现的每一个弱点，则都是一个改进目标。</p>
<h4 id="混沌工程也有几个高级原则："><a href="#混沌工程也有几个高级原则：" class="headerlink" title="混沌工程也有几个高级原则："></a>混沌工程也有几个高级原则：</h4><p>使用改变现实世界的事件，就是要在真实的场景中进行实验，而不要想象和构造一些假想和假设的场景；</p>
<p>在生产环境运行，为了发现真实场景的弱点，所以更建议在生产环境运行这些实验；</p>
<p>自动化连续实现，人工的手工操作是劳动密集型的、不可持续的，因此要把混沌工程自动化构建到系统中；</p>
<p>最小爆破半径，与第二条配合，要尽量减少对用户的负面影响，即使不可避免，也要尽力减少范围和程度。</p>
<p>这样，就更符合持续交付的需求和胃口了。</p>
<h3 id="Netflix-公司的先驱实践"><a href="#Netflix-公司的先驱实践" class="headerlink" title="Netflix 公司的先驱实践"></a>Netflix 公司的先驱实践</h3><p>Netflix 为了保证其系统在 AWS 上的健壮性，创造了 Chaos Monkey，可以说是混沌工程真正的先驱者。</p>
<p>Chaos Monkey 会在工作日期间，随机地杀死一些服务以制造混乱，从而检验系统的稳定性。而工程师们不得不停下手头工作去解决这些问题，并且保证它们不会再现。久而久之，系统的健壮性就可以不断地被提高。</p>
<p>Netflix 公司有一句名言，叫作“避免失败的最好办法就是经常失败”。所以，Chaos Monkey 会在日常反复持续执行，真正地持续融合在系统中。这，也为其持续交付中的测试提供了很好的借鉴。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>破坏性测试能够很好地测试分布式系统的健壮性，但也因为其破坏特点，使得它在持续交付中无法显示真正的威力；而混沌工程的提出，很好地解决了这个问题，使破坏性测试的威力能够在持续交付过程中被真正发挥出来。</p>
<p>混沌工程的一个典型实践是，Netflix 公司的 Chaos Monkey 系统。这个系统已经证明了混沌工程的价值和重要性，值得我们借鉴。</p>

    </div>

  </article>
  <div class="toc-container">
    
  <div id="toc" class="toc-article">
    <strong class="toc-title">目录</strong>
    <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#什么是破坏性测试？"><span class="toc-text">什么是破坏性测试？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#破坏性测试的流程与用例设计"><span class="toc-text">破坏性测试的流程与用例设计</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#破坏性测试的执行策略"><span class="toc-text">破坏性测试的执行策略</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#混沌工程"><span class="toc-text">混沌工程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#混沌工程也有几个高级原则："><span class="toc-text">混沌工程也有几个高级原则：</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Netflix-公司的先驱实践"><span class="toc-text">Netflix 公司的先驱实践</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#总结"><span class="toc-text">总结</span></a></li></ol>
  </div>


  </div>
</div>
<script type="text/javascript" src="//rf.revolvermaps.com/0/0/8.js?i=5sr5du46f27&amp;m=0&amp;c=ff0000&amp;cr1=ffffff&amp;f=arial&amp;l=33" async="async"></script>
<div class="copyright">
    <span>本作品采用</span>
    <a href="https://creativecommons.org/licenses/by/4.0/">知识共享署名 4.0 国际许可协议</a>
    <span>进行许可。 转载时请注明原文链接。</span>
</div>


  
    <div class="post-nav" style="margin-left:-168px;">
      <div class="post-nav-item post-nav-next">
        
          <span>〈 </span>
          <a href="/2020/08/14/Gin框架路由的拆分/" rel="next" title="Gin框架路由的拆分和注册">
          Gin框架路由的拆分和注册
          </a>
        
      </div>
  
      <div class="post-nav-item post-nav-prev">
          
          <a href="/2020/08/27/k8sAPI的使用/" rel="prev" title="python之kubernetes api 使用">
            python之kubernetes api 使用
          </a>
          <span>〉</span>
        
      </div>
    </div>
  


	
	<div style="width:109%; margin-left:-144px" id="lv-container" data-id="city" data-uid="MTAyMC80OTg5NS8yNjM4Ng==">
	<script type="text/javascript">
   	   (function(d, s) {
       		var j, e = d.getElementsByTagName(s)[0];

       		if (typeof LivereTower === 'function') { return; }

       		j = d.createElement(s);
       		j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
       		j.async = true;

       		e.parentNode.insertBefore(j, e);
   	   })(document, 'script');
	</script>
	<noscript> 为正常使用来必力评论功能请激活JavaScript</noscript>
        </div>
	



    </div>

    

  </div>
  <footer class="footer text-center">
    <div id="bottom-inner">
        <a class="bottom-item" href target="_blank">GitHub</a> |
        <a class="bottom-item" href>友情链接</a> |
        <a class="bottom-item" href="https://hexo.io" target="_blank">Powered by hexo</a> |
        <a class="bottom-item" href="https://github.com/fooying/hexo-theme-xoxo-plus" target="_blank">Theme xoxo-plus</a> |
        <a class="bottom-item" href="/atom.xml">订阅</a>
    </div>
</footer>

  

<script>
  (function(window, document, undefined) {

    var timer = null;

    function returnTop() {
      cancelAnimationFrame(timer);
      timer = requestAnimationFrame(function fn() {
        var oTop = document.body.scrollTop || document.documentElement.scrollTop;
        if (oTop > 0) {
          document.body.scrollTop = document.documentElement.scrollTop = oTop - 50;
          timer = requestAnimationFrame(fn);
        } else {
          cancelAnimationFrame(timer);
        }
      });
    }

    var hearts = [];
    window.requestAnimationFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          setTimeout(callback, 1000 / 60);
        }
    })();
    init();

    function init() {
      css(".heart{z-index:9999;width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
      attachEvent();
      gameloop();
      addMenuEvent();
    }

    function gameloop() {
      for (var i = 0; i < hearts.length; i++) {
        if (hearts[i].alpha <= 0) {
          document.body.removeChild(hearts[i].el);
          hearts.splice(i, 1);
          continue;
        }
        hearts[i].y--;
        hearts[i].scale += 0.004;
        hearts[i].alpha -= 0.013;
        hearts[i].el.style.cssText = "left:" + hearts[i].x + "px;top:" + hearts[i].y + "px;opacity:" + hearts[i].alpha + ";transform:scale(" + hearts[i].scale + "," + hearts[i].scale + ") rotate(45deg);background:" + hearts[i].color;
      }
      requestAnimationFrame(gameloop);
    }

    /**
     * 给logo设置点击事件
     * 
     * - 回到顶部
     * - 出现爱心
     */
    function attachEvent() {
      var old = typeof window.onclick === "function" && window.onclick;
      var logo = document.getElementById("logo");
      if (logo) {
        logo.onclick = function(event) {
          returnTop();
          old && old();
          createHeart(event);
        }
      }
      
    }

    function createHeart(event) {
      var d = document.createElement("div");
      d.className = "heart";
      hearts.push({
        el: d,
        x: event.clientX - 5,
        y: event.clientY - 5,
        scale: 1,
        alpha: 1,
        color: randomColor()
      });
      document.body.appendChild(d);
    }

    function css(css) {
      var style = document.createElement("style");
      style.type = "text/css";
      try {
        style.appendChild(document.createTextNode(css));
      } catch (ex) {
        style.styleSheet.cssText = css;
      }
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function randomColor() {
      // return "rgb(" + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + ")";
      return "#F44336";
    }

    function addMenuEvent() {
      var menu = document.getElementById('menu-main-post');
      if (menu) {
        var toc = document.getElementById('toc');
        if (toc) {
          menu.onclick = function() {
            if (toc) {
              if (toc.style.display == 'block') {
                toc.style.display = 'none';
              } else {
                toc.style.display = 'block';
              }
            }
          };
        } else {
          menu.style.display = 'none';
        }
      }
    }

  })(window, document);
</script>

  



  

</body>
</html>
